<?php
namespace Lib\Pay;

final class Wechat {
    use \Lib\Traits\Instance;

    private function getAccessToken() {
        $url    = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . \Lib\Pay\WxPayConfig::appid() .
            "&secret=" . \Lib\Pay\WxPayConfig::appsecret();
        $json   = file_get_contents($url);
        $result = json_decode($json, true);

        if(!isset($result['access_token'])) {
            exit('获取access_token失败！');
        }

        return $result["access_token"];
    }

    protected function config($openId) {
        $this->setData('nonce_str', $this->getNonceStr());
        $this->setData('sign', $this->getSign());
        $this->setData('mch_billno', \Lib\Pay\WxPayConfig::mchid() . date('YmdHis') .
            substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 10));
        $this->setData('mch_id', \Lib\Pay\WxPayConfig::mchid());
        $this->setData('wxappid', \Lib\Pay\WxPayConfig::appid());
        $this->setData('send_name', '搞起app');
        $this->setData('re_openid', $openId);
        $this->setData('total_amount', 1);
        $this->setData('total_num', 1);
        $this->setData('wishing', '感谢您参加猜灯谜活动，祝您元宵节快乐！');

        $sessionAdmin = \Yaf\Session::getInstance()->get('Admin');

        if(!isset($sessionAdmin['ip'])) {
            $sessionAdmin['ip'] = '0.0.0.0';
        }

        $this->setData('client_ip', $sessionAdmin['ip']);
        $this->setData('act_name', '猜灯谜抢红包活动');
        $this->setData('remark', '猜越多得越多，快来抢！');
    }

    public function envelope() {
        $params['nonce_str'] = $this->getData('nonce_str');
        $params['sign']      = $this->getData('sign');

        try {
            $http = new \Lib\Http();
            $http->setPostFields($params);
            $http->setUrl('https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack');
            $http->setTimeout(3);
            $returnMsg = json_decode($http->send());
        } catch(\Exception $exc) {
            $msg['status'] = false;
            $returnMsg     = new \Lib\Std($msg);
        }

        return $returnMsg;
    }

    protected $values = array();

    public function ToUrlParams() {
        $buff = "";
        foreach($this->values as $k => $v) {
            if($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    public function makeSign() {
        ksort($this->values);
        $string = $this->ToUrlParams();
        $string = $string . "&key=" . \Lib\Pay\WxPayConfig::key();
        $string = md5($string);
        $result = strtoupper($string);
        return $result;
    }

    public function setSign() {
        $sign                 = $this->makeSign();
        $this->values['sign'] = $sign;
        return $sign;
    }

    public function getSign() {
        if(!array_key_exists('sign', $this->values)) {
            $this->SetSign();
        }

        return $this->values['sign'];
    }

    public static function getNonceStr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str   = "";
        for($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    public function setData($key, $value) {
        $this->values[$key] = $value;
    }

    public function getData($key) {
        return $this->values[$key];
    }

    protected function payConfig($openId, $sum) {
        $this->setData('mch_appid', \Lib\Pay\WxPayConfig::appid());
        $this->setData('mchid', \Lib\Pay\WxPayConfig::mchid());
        $this->setData('device_info', '');
        $this->setData('nonce_str', $this->getNonceStr());

        $this->setData('partner_trade_no', \Lib\Pay\WxPayConfig::mchid() . date('Ymd') .
            substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 10));
        $this->setData('openid', $openId);
        $this->setData('check_name', 'NO_CHECK');
        $this->setData('re_user_name', '');
        $this->setData('amount', $sum); // 分
        $this->setData('desc', '您申请的提现已通过审核~');

        $sessionAdmin = \Yaf\Session::getInstance()->get('Admin');

        if(!isset($sessionAdmin['ip'])) {
            $sessionAdmin['ip'] = '0.0.0.0';
        }

        $this->setData('spbill_create_ip', $sessionAdmin['ip']);
        $this->setData('sign', $this->getSign());
    }

    public function pay($openId, $sum) {
        $this->payConfig($openId, $sum);

        try {
            $url         = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
            $responseXml = $this->postXmlCurl($this->toXml(), $url, true);

            $returnMsg = $this->fromXml($responseXml);
        } catch(\Exception $exc) {
            return '提现异常';
        }

        if(isset($returnMsg['result_code']) && $returnMsg['result_code'] === 'FAIL') {
            return isset($returnMsg['return_msg']) ? $returnMsg['return_msg'] : '提现失败';
        }

        if(isset($returnMsg['result_code']) && $returnMsg['result_code'] === 'SUCCESS') {
            $model = new \WithdrawLogModel();
            $model->setTrade_no(isset($returnMsg['partner_trade_no']) ? $returnMsg['partner_trade_no'] : '');
            $model->setWechat_no(isset($returnMsg['payment_no']) ? $returnMsg['payment_no'] : '');
            $model->setAdd_time(isset($returnMsg['payment_time']) ? strtotime($returnMsg['payment_time']) : '');

            try {
                $id = \Mapper\WithdrawLogModel::getInstance()->insert($model);
            } catch(\Exception $e) {
                $id = 0;
            }

            return true;
        }

        return '数据有误, 请重试';
    }

    public function fromXml($xml) {
        if(!$xml) {
            throw new \Exception("xml数据异常！");
        }
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    public function toXml() {
        if(!is_array($this->values) || count($this->values) <= 0) {
            throw new \Exception("数组数据异常！");
        }

        $xml = "<xml>";
        foreach($this->values as $key => $val) {
            if(is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";

        return $xml;
    }

    protected function postXmlCurl($xml, $url, $useCert = false, $second = 30) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        if(\Lib\Pay\WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
            && \Lib\Pay\WxPayConfig::CURL_PROXY_PORT != 0
        ) {
            curl_setopt($ch, CURLOPT_PROXY, \Lib\Pay\WxPayConfig::CURL_PROXY_HOST);
            curl_setopt($ch, CURLOPT_PROXYPORT, \Lib\Pay\WxPayConfig::CURL_PROXY_PORT);
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if($useCert == true) {
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, APPLICATION_PATH . \Lib\Pay\WxPayConfig::SSLCERT_PATH);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, APPLICATION_PATH . \Lib\Pay\WxPayConfig::SSLKEY_PATH);
        }

        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);

        if($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new \Exception("curl出错，错误码:$error");
        }
    }

    public function doSend($touser, $templateId, $url, $data, $topColor = '#7B68EE') {
        if($templateId !== \Lib\Consts::TEMP_ID_REFUND) {
            return false;
        }

        if(!array_key_exists('order_no', $data) || !array_key_exists('time', $data)
            || !array_key_exists('cancel_time', $data) || !array_key_exists('price', $data)
        ) {
            return false;
        }

        if(isset($data['type']) && $data['type'] === 'sell') {
            $tmpData = [
                'first'    => ['value' => '您有一笔交易被系统取消，金额将自动退款给付款用户，详情如下：'],
                'keyword1' => ['value' => $data['order_no']],
                'keyword2' => ['value' => $data['time']],
                'keyword3' => ['value' => $data['cancel_time']],
                'keyword4' => ['value' => $data['price'] . '元'],
                'remark'   => ['value' => '如有任何疑问，请直接咨询搞起在线微信客服~'],
            ];
        } elseif(isset($data['type']) && $data['type'] === 'buy') {
            $tmpData = [
                'first'    => ['value' => '您有一笔订单被系统取消，退款如下：'],
                'keyword1' => ['value' => $data['order_no']],
                'keyword2' => ['value' => $data['time']],
                'keyword3' => ['value' => $data['cancel_time']],
                'keyword4' => ['value' => $data['price'] . '元'],
                'remark'   => ['value' => '如有任何疑问，请直接咨询搞起在线微信客服~'],
            ];
        }

        if(empty($tmpData)) {
            return false;
        }

        $params['touser']      = $touser;
        $params['template_id'] = $templateId;
        $params['url']         = $url;
        $params['topcolor']    = $topColor;
        $params['data']        = $tmpData;

        try {
            $http = new \Lib\Http();
            $http->setPostFields(json_encode($params));
            $http->setUrl("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $this->getAccessToken());
            $http->setTimeout(3);
            $returnMsg = json_decode($http->send());
        } catch(\Exception $exc) {
            $msg['status'] = false;
            $returnMsg     = new \Lib\Std($msg);
        }

        return $returnMsg;
    }

}
